-- {-# LANGUAGE RankNTypes #-}

module CPS where

import Data.Function

-- 1. Any type

{-

newtype CPS r a = CPS {
    runCPS :: (a -> r) -> r
}

-- toCPS :: a -> CPS r a
-- toCPS :: a -> CPS a a
toCPS a = CPS (\c -> c a)

-- fromCPS :: CPS r a -> a
-- fromCPS :: CPS a a -> a
fromCPS (CPS c) = c id

-}

newtype CPS a = CPS {
    runCPS :: (a -> a) -> a
}

toCPS :: a -> CPS a
toCPS a = CPS (\c -> c a)

fromCPS :: CPS a -> a
fromCPS (CPS c) = c id


-- 2. Maybe a

isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing _ = False

isJust :: Maybe a -> Bool
isJust = not. isNothing

{-

newtype MaybeCPS r a = MaybeCPS {
    runMaybeCPS :: (a -> r) -> r -> r
}

-- toMaybeCPS :: Maybe a -> MaybeCPS r a
-- toMaybeCPS :: Maybe a -> MaybeCPS (Maybe a) a
toMaybeCPS (Just x) = MaybeCPS (\ks kf -> ks x)
toMaybeCPS Nothing = MaybeCPS (\ks kf -> kf)

-- fromMaybeCPS :: MaybeCPS r a -> r
-- fromMaybeCPS :: MaybeCPS (Maybe a) a -> Maybe a
fromMaybeCPS (MaybeCPS c) = c Just Nothing

-}

newtype MaybeCPS a = MaybeCPS {
    runMaybeCPS :: (a -> Maybe a) -> Maybe a -> Maybe a
}

toMaybeCPS :: Maybe a -> MaybeCPS a
toMaybeCPS (Just x) = MaybeCPS (\ks kf -> ks x)
toMaybeCPS Nothing = MaybeCPS (\ks kf -> kf)

fromMaybeCPS :: MaybeCPS a -> Maybe a
fromMaybeCPS (MaybeCPS c) = c Just Nothing

-- 3. Data container

data Container = C Int Int deriving (Show, Eq)

{-

newtype ContainerCPS r = ContainerCPS {
    runContainerCPS :: (Int -> Int -> r) -> r
}

-- toContainerCPS :: Container -> ContainerCPS r
-- toContainerCPS :: Container -> ContainerCPS Container
toContainerCPS (C i1 i2) = ContainerCPS (\k -> k i1 i2)

-- fromContainerCPS :: ContainerCPS r -> Container
-- fromContainerCPS :: ContainerCPS Container -> Container
fromContainerCPS (ContainerCPS c) = c (\x y -> C x y)

-}

newtype ContainerCPS = ContainerCPS {
    runContainerCPS :: (Int -> Int -> Container) -> Container
}

toContainerCPS :: Container -> ContainerCPS
toContainerCPS (C i1 i2) = ContainerCPS (\k -> k i1 i2)

fromContainerCPS :: ContainerCPS -> Container
fromContainerCPS (ContainerCPS c) = c (\x y -> C x y)


-- 4. Either a b

{-

newtype EitherCPS r a b = EitherCPS {
    runEitherCPS :: (a -> r) -> (b -> r) -> r
}

-}

newtype EitherCPS a b = EitherCPS {
    runEitherCPS :: (a -> Either a b) -> (b -> Either a b) -> Either a b
}

toEitherCPS :: Either a b -> EitherCPS a b
toEitherCPS (Left x) = EitherCPS (\kl kr -> kl x)
toEitherCPS (Right y) = EitherCPS (\kl kr -> kr y)

fromEitherCPS :: EitherCPS a b -> Either a b
fromEitherCPS (EitherCPS e) = e Left Right